#include "xxeditor.h"
#include "BoxList.h"
#include "GLWidget.h"
#include "qfiledialog.h"

#include "qttreepropertybrowser.h"
#include "qtgroupboxpropertybrowser.h"
#include "qtbuttonpropertybrowser.h"
#include "qtvariantproperty.h"
#include "qtpropertybrowser.h"
#include "Router.h"
#include "qtpropertymanager.h"
#include "qteditorfactory.h"
#include "StringUtil.h"
#include "qicon.h"
#include <QScrollArea>
#include "qevent.h"
#include "qtimer.h"
#include "QSettings"
#include "EditorScene.h"

#include "QColorDialog"
#include "Command.h"
#include "Selections.h"
#include "Accesser.h"
#include "PathSetting.h"
#include "CCLuaEngine.h"
#include "Options.h"
#include "QClipboard.h"

class XXPrivate
{
public:
    XXPrivate() : browser(nullptr) {}

    QtDoublePropertyManager* doubleManager;
    QtStringPropertyManager* stringManager;
    QtColorPropertyManager* colorManager;
    QtPointFPropertyManager* pointManager;
    QtSizeFPropertyManager* sizeManager;
    QtBoolPropertyManager* boolManager;
    QtIntPropertyManager* intManager;
    QtRectFPropertyManager* rectManager;
    QtEnumPropertyManager* enumManager;
    QtFileEditPropertyManager* fileManager;

    QtAbstractPropertyBrowser* browser;

    QMap<QString, QtProperty*> nameToProp;
    QMap<QtProperty*, QString> propToType;
};

XXEditor::XXEditor(QWidget *parent)
    : QMainWindow(parent)
    , _widgetRoot(nullptr)
    , _d(new XXPrivate)
{
    new Selection;
    new Router;
    new cmd_mgr;

    ui.setupUi(this);

    readSettings();

    createGL();
    createTree();
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
    setFactory("Group");
#else//maybe in mac tree was better?
    setFactory("Tree");
#endif

    connect(Router::getInstancePtr(), SIGNAL(onAddProp(const char*, const char*, const char*, const char*, bool)),
        this, SLOT(addProp(const char*, const char*, const char*, const char*, bool)));
    connect(Router::getInstancePtr(), SIGNAL(onSetProp(const char*, const char*)),
        this, SLOT(setProp(const char*, const char*)));
    connect(Router::getInstancePtr(), SIGNAL(onClearProp()),
        this, SLOT(clearProp()));
}


XXEditor::~XXEditor()
{
    delete _d;

    delete cmd_mgr::getInstancePtr();
    delete Router::getInstancePtr();
    delete Selection::getInstancePtr();
}

void XXEditor::updateProp(const QString& name)
{
    setProp(name.toUtf8(), Accesser::get(Selection::getInstance().getCurrent(), name).toUtf8());
}

void XXEditor::updateValue(TreeItem* item)
{
    setProp("Name", (const char*)item->text(0).toUtf8());
    setProp("Visible", item->checkState(0) == Qt::Checked ? "true" : "false");
}

void XXEditor::currentChanged()
{
    auto set = Selection::getInstance().get();
    if (set.size() == 1)
        Router::getInstance().onSelectedItem(_boxList->current());
    else
        clearProp();
}

void XXEditor::createTree()
{
    _boxList = new BoxList(NULL);
    ui.dockWidget->setWidget(_boxList);

    connect(Selection::getInstancePtr(), SIGNAL(onSelectedChange()),
        _boxList, SLOT(onSelectionChanged()));

    connect(Selection::getInstancePtr(), SIGNAL(onSelectedChange()),
        this, SLOT(currentChanged()));

    connect(_boxList, SIGNAL(onItemTextChanged(TreeItem*)), this, SLOT(updateValue(TreeItem*)));
    
    connect(_boxList, SIGNAL(onAddItem(TreeItem*, const QString&, const QString&)),
        Router::getInstancePtr(), SLOT(onAddItem(TreeItem*, const QString&, const QString&)));
    connect(_boxList, SIGNAL(onDeleteItem(TreeItem*)), this, SLOT(onDeleteItem(TreeItem*)));
}

void XXEditor::onDeleteItem(TreeItem* item)
{
    Selection::getInstance().remove(item->getWidget());
    item->getWidget()->removeFromParent();
    clearProp();
}

void XXEditor::loadCC()
{

    connect(_gl, SIGNAL(currentPropChange(const QString&)), this, SLOT(updateProp(const QString&)));

    _gl->init(_scripsPath);
    _gl->setBGColor(_bgColor);
    _boxList->root()->setWidget(_gl->getRoot());

    createActions();
}

void XXEditor::createGL()
{
    _gl = new EditorScene(this);

    _gl->setFocusPolicy(Qt::ClickFocus);
    _gl->setWindowFlags(_gl->windowFlags() & ~Qt::WindowMaximizeButtonHint);
    _gl->setMinimumSize(500, 500);
    setCentralWidget(_gl);
    _gl->InitGL();

    QTimer* delay = new QTimer(this);
    connect(delay, SIGNAL(timeout()), this, SLOT(loadCC()));
    delay->setSingleShot(true);
    delay->start(20);
}

void XXEditor::createActions()
{
    QAction* saveAction = new QAction(QIcon(":/XXEditor/images/save.png"), "&Save", this);
    saveAction->setShortcut(QKeySequence::Save);

    QAction* openAction = new QAction(QIcon(":/XXEditor/images/open.png"), "&Open", this);
    QAction* newAction = new QAction(QIcon(":/XXEditor/images/new.png"), "&New", this);
    QAction* saveCodeAction = new QAction("Save &Code", this);

    QAction* ReloadAction = new QAction(QIcon(":/XXEditor/images/refresh.png"), "&Reload", this);

    QAction* XAction = new QAction(QIcon(":/XXEditor/images/centerx.png"), "&X", this);
    QAction* YAction = new QAction(QIcon(":/XXEditor/images/centery.png"), "&Y", this);

    QAction* HAction = new QAction(QIcon(":/XXEditor/images/h.png"), "&H", this);
    QAction* VAction = new QAction(QIcon(":/XXEditor/images/v.png"), "&V", this);

    QAction* ZoomIn = new QAction(QIcon(":/XXEditor/images/zoomin.png"), "&I", this);
    QAction* ZoomOut = new QAction(QIcon(":/XXEditor/images/zoomout.png"), "&T", this);



    QAction* setColorAction = new QAction("&BG Color", this);
    QAction* setWorkpathAct = new QAction("&Work Path", this);

    QAction* showboxAction = new QAction("DebugBoxes", this);
    showboxAction->setCheckable(true);
    showboxAction->setChecked(Options::current.boxVisible);

    QAction* redoAction = new QAction(QIcon(":/XXEditor/images/redo.png"), "&Redo", this);
    redoAction->setShortcut(QKeySequence::Redo);
    QAction* undoAction = new QAction(QIcon(":/XXEditor/images/undo.png"), "&Undo", this);
    undoAction->setShortcut(QKeySequence::Undo);

    connect(redoAction, SIGNAL(triggered()), cmd_mgr::getInstancePtr(), SLOT(redo_()));
    connect(undoAction, SIGNAL(triggered()), cmd_mgr::getInstancePtr(), SLOT(undo_()));
   
    connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
    connect(saveCodeAction, SIGNAL(triggered()), this, SLOT(saveCode()));
    connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
    connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
    connect(ReloadAction, SIGNAL(triggered()), this, SLOT(reload()));

    connect(setColorAction, SIGNAL(triggered()), this, SLOT(setBgColor()));
    connect(setWorkpathAct, SIGNAL(triggered()), this, SLOT(setWorkPath()));
    connect(showboxAction, SIGNAL(triggered(bool)), this, SLOT(setBoxShow(bool)));

    connect(XAction, SIGNAL(triggered()), _gl, SLOT(aliginX()));
    connect(YAction, SIGNAL(triggered()), _gl, SLOT(aliginY()));
    connect(HAction, SIGNAL(triggered()), _gl, SLOT(aliginH()));
    connect(VAction, SIGNAL(triggered()), _gl, SLOT(aliginV()));

    connect(ZoomIn, SIGNAL(triggered()), _gl, SLOT(zoomIn()));
    connect(ZoomOut, SIGNAL(triggered()), _gl, SLOT(zoomOut()));


    ui.mainToolBar->addAction(newAction);
    ui.mainToolBar->addAction(openAction);
    ui.mainToolBar->addAction(saveAction);
    ui.mainToolBar->addAction(ReloadAction);

    QToolBar* toolbar = addToolBar("Alignment");

    toolbar->addAction(XAction);
    toolbar->addAction(YAction);
    toolbar->addAction(HAction);
    toolbar->addAction(VAction);
    toolbar->addAction(ZoomIn);
    toolbar->addAction(ZoomOut);

    QToolBar* toolbar2 = addToolBar("Redo/Undo");

    toolbar2->addAction(redoAction);
    toolbar2->addAction(undoAction);

    ui.menuFile->addAction(newAction);
    ui.menuFile->addAction(openAction);
    ui.menuFile->addAction(saveAction);
    ui.menuFile->addAction(saveCodeAction);

    ui.menuOptions->addAction(setColorAction);
    ui.menuOptions->addAction(setWorkpathAct);
    ui.menuOptions->addAction(showboxAction);

    connect(ui.menuStyle_2, SIGNAL(triggered(QAction*)), this, SLOT(setStyle(QAction*)));
}

void XXEditor::onSetPath(const QString& w, const QString& s)
{
    _workPath = w;


    lua_State* L = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();

    lua_getglobal(L, "package");                                  /* L: package */
    lua_getfield(L, -1, "path");                /* get package.path, L: package path */
    QString cur_path = lua_tostring(L, -1);
    
    cur_path = cur_path.replace(_scripsPath.toUtf8(), s.toUtf8());

    lua_pushlstring(L, cur_path.toUtf8(), cur_path.size());
    lua_setfield(L, -3, "path");          /* package.path = newpath, L: package path */
    lua_pop(L, 2);

    _scripsPath = s;
}

void XXEditor::setWorkPath()
{
    PathSetting* ps = new PathSetting();
    ps->set(_workPath, _scripsPath);

    connect(ps, SIGNAL(onSet(const QString&, const QString&)),
        this, SLOT(onSetPath(const QString&, const QString&)));

    ps->setWindowModality(Qt::ApplicationModal);
    ps->show();
}

void XXEditor::setBgColor()
{
    _bgColor = QColorDialog::getColor(Qt::white, this);
    _gl->setBGColor(_bgColor);
}

void XXEditor::newFile()
{
    QDir d;

    if (!_currentSaveFile.isEmpty() && !_currentSaveFile.isNull())
        d = QDir(QFileInfo(_currentSaveFile).dir());
    else
        d = QDir(_workPath);


	QString filePath = QFileDialog::getSaveFileName(this,
		tr("Choose a File"),
		d.absoluteFilePath("File.xx"),
		tr("XXFiles (*.xx)"));

    setCurrentFile(filePath);
}

static void save(const QString& s, QString& f)
{
    QFile file(f);

    file.open(QIODevice::WriteOnly);
    file.write(s.toUtf8());
    file.close();
}

void XXEditor::save()
{
    if (_currentSaveFile.isNull() || _currentSaveFile.isEmpty())
    {
        newFile();
        if (_currentSaveFile.isNull() || _currentSaveFile.isEmpty())
        {
            ui.statusBar->showMessage(QString("Not Saved"), 5);
            return;
        }
    }

    ::save(Accesser::toString(_boxList->getTopLevelItems()), _currentSaveFile);
    ui.statusBar->showMessage(QString("Saved [%1]").arg(_currentSaveFile), 5);
}

void XXEditor::saveCode()
{
    QDir d;

    if (!_currentSaveFile.isEmpty() && !_currentSaveFile.isNull())
        d = QDir(QFileInfo(_currentSaveFile).dir());
    else
        d = QDir(_workPath);

    QString filePath = QFileDialog::getSaveFileName(this, tr("Choose a File"), d.absoluteFilePath("File.lua"));

    if (!filePath.isEmpty() || !filePath.isNull())
    {
        ::save(Accesser::getCode(_boxList->getTopLevelItems()), filePath);
    }
}

void XXEditor::open()
{
    QString filePath = QFileDialog::getOpenFileName(this, tr("Choose a File"), _workPath, tr("XXFiles (*.xx);; Json(*.ExportJson *.json);; Csb(*.csb)"));
    if (filePath.isNull())
        return;

    setCurrentFile(filePath);
    _gl->doSelected(nullptr);
    _boxList->onOpen(Accesser::fromFile(filePath));
}

void XXEditor::reload()
{
    setCurrentFile(_currentSaveFile);
    _gl->doSelected(nullptr);
    _boxList->onOpen(Accesser::fromFile(_currentSaveFile));
}

void XXEditor::setFactory(const QString& name)
{
    if (_widgetRoot)
        delete _widgetRoot;

    if (name == "Tree")
    {
        _d->browser = new QtTreePropertyBrowser(ui.dockWidget_2);
        _widgetRoot = _d->browser;
        ui.dockWidget_2->setWidget(_d->browser);
    }
    else
    {
        if (name == "Group")
            _d->browser = new QtGroupBoxPropertyBrowser(nullptr);
        else
            _d->browser = new QtButtonPropertyBrowser(nullptr);
        QScrollArea *scroll4 = new QScrollArea();
        scroll4->setWidgetResizable(true);
        scroll4->setWidget(_d->browser);

        _widgetRoot = scroll4;

        ui.dockWidget_2->setWidget(scroll4);
    }
    createProp();
}

void XXEditor::setStyle(QAction* a)
{
    if (a != ui.actionGroup_2) 
        ui.actionGroup_2->setChecked(false);
    if (a != ui.actionTree_2) 
        ui.actionTree_2->setChecked(false);
    if (a != ui.actionButton_3) 
        ui.actionButton_3->setChecked(false);

    setFactory(a->text());
}

void XXEditor::createProp()
{

    _d->doubleManager = new QtDoublePropertyManager(this);
    _d->stringManager = new QtStringPropertyManager(this);
    _d->colorManager = new QtColorPropertyManager(this);
    _d->pointManager = new QtPointFPropertyManager(this);
    _d->sizeManager = new QtSizeFPropertyManager(this);
    _d->boolManager = new QtBoolPropertyManager(this);
    _d->intManager = new QtIntPropertyManager(this);
    _d->fileManager = new QtFileEditPropertyManager(this);
    _d->rectManager = new QtRectFPropertyManager(this);
    _d->enumManager = new QtEnumPropertyManager(this);

    connect(_d->doubleManager, SIGNAL(valueChanged(QtProperty *, double)),
        this, SLOT(valueChanged(QtProperty *, double)));
    connect(_d->stringManager, SIGNAL(valueChanged(QtProperty *, const QString &)),
        this, SLOT(valueChanged(QtProperty *, const QString &)));
    connect(_d->colorManager, SIGNAL(valueChanged(QtProperty *, const QColor &)),
        this, SLOT(valueChanged(QtProperty *, const QColor &)));
    connect(_d->pointManager, SIGNAL(valueChanged(QtProperty *, const QPointF &)),
        this, SLOT(valueChanged(QtProperty *, const QPointF &)));
    connect(_d->sizeManager, SIGNAL(valueChanged(QtProperty *, const QSizeF &)),
        this, SLOT(valueChanged(QtProperty *, const QSizeF &)));
    connect(_d->boolManager, SIGNAL(valueChanged(QtProperty *, bool)),
        this, SLOT(valueChanged(QtProperty *, bool)));
    connect(_d->intManager, SIGNAL(valueChanged(QtProperty *, int)),
        this, SLOT(valueChanged(QtProperty *, int)));
    connect(_d->fileManager, SIGNAL(valueChanged(QtProperty *, const QString&)),
        this, SLOT(valueChanged(QtProperty *, const QString&)));
    connect(_d->rectManager, SIGNAL(valueChanged(QtProperty *, const QRectF&)),
        this, SLOT(valueChanged(QtProperty *, const QRectF&)));
    connect(_d->enumManager, SIGNAL(valueChanged(QtProperty*, int)),
        this, SLOT(valueChanged(QtProperty *, int)));

    connect(_d->fileManager, SIGNAL(onButtonClick(QtProperty*)), 
        this, SLOT(onLoadFileButtonClick(QtProperty *)));

    QtDoubleSpinBoxFactory *doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this);

    auto fileFactory = new QtFileEditFactory(this);

    QtCheckBoxFactory *checkBoxFactory = new QtCheckBoxFactory(this);
    QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory(this);
    QtLineEditFactory *lineEditFactory = new QtLineEditFactory(this);
    QtEnumEditorFactory *comboBoxFactory = new QtEnumEditorFactory(this);

    _d->browser->setFactoryForManager(_d->fileManager, fileFactory);
    _d->browser->setFactoryForManager(_d->doubleManager, doubleSpinBoxFactory);
    _d->browser->setFactoryForManager(_d->intManager, spinBoxFactory);
    _d->browser->setFactoryForManager(_d->stringManager, lineEditFactory);
    _d->browser->setFactoryForManager(_d->boolManager, checkBoxFactory);
    _d->browser->setFactoryForManager(_d->rectManager->subDoublePropertyManager(), doubleSpinBoxFactory);
    _d->browser->setFactoryForManager(_d->colorManager->subIntPropertyManager(), spinBoxFactory);
    _d->browser->setFactoryForManager(_d->pointManager->subDoublePropertyManager(), doubleSpinBoxFactory);
    _d->browser->setFactoryForManager(_d->sizeManager->subDoublePropertyManager(), doubleSpinBoxFactory);
    _d->browser->setFactoryForManager(_d->enumManager, comboBoxFactory);
}

void XXEditor::clearProp()
{
    _d->browser->clear();
    _d->nameToProp.clear();
}

static void setValueFromString(XXPrivate* xx, QtProperty* p, const QString& type, const char* value)
{
    if (type == "string")   xx->stringManager->setValue(p, value);
    else if (type == "int") xx->intManager->setValue(p, StringUtil::parseValue<int>(value));
    else if (type == "float") xx->doubleManager->setValue(p, StringUtil::parseValue<float>(value));
    else if (type == "bool") xx->boolManager->setValue(p, StringUtil::parseValue<bool>(value));
    else if (type == "pos") xx->pointManager->setValue(p, StringUtil::parseValueEx2<QPointF, float>(value));
    else if (type == "size") xx->sizeManager->setValue(p, StringUtil::parseValueEx2<QSizeF, float>(value));
    else if (type == "color") xx->colorManager->setValue(p, StringUtil::parseValueEx3<QColor, int>(value));
    else if (type == "color4") xx->colorManager->setValue(p, StringUtil::parseValueEx4<QColor, int>(value));
    else if (type == "rect") xx->rectManager->setValue(p, StringUtil::parseValueEx4<QRectF, float>(value));
    else if (type == "file") xx->fileManager->setValue(p, value);
    else if (type == "enum") xx->enumManager->setValue(p, StringUtil::parseValue<int>(value));
}

void XXEditor::setProp(const char* name, const char* value)
{
    const auto& prop_iter = _d->nameToProp.find(name);
    if (prop_iter != _d->nameToProp.end())
    {
        const auto& type_iter = _d->propToType.find(prop_iter.value());
        if (type_iter != _d->propToType.end())
        {
            setValueFromString(_d, prop_iter.value(), type_iter.value(), value);
            return;
        }
    }
}

#define streq(a, b) (strcmp(a,b)==0)

void XXEditor::addProp(const char* name, const char* type, const char* value, const char* choices, bool readyonly)
{
    QtProperty* p;

    if (streq(type, "string"))   p = _d->stringManager->addProperty(name);
    else if (streq(type, "int")) p = _d->intManager->addProperty(name);
    else if (streq(type, "float")) p = _d->doubleManager->addProperty(name);
    else if (streq(type, "bool")) p = _d->boolManager->addProperty(name);
    else if (streq(type, "pos")) p = _d->pointManager->addProperty(name);
    else if (streq(type, "size")) p = _d->sizeManager->addProperty(name);
    else if (streq(type, "color")) p = _d->colorManager->addProperty(name);
    else if (streq(type, "color4")) p = _d->colorManager->addProperty(name);
    else if (streq(type, "file")) p = _d->fileManager->addProperty(name);
    else if (streq(type, "rect")) p = _d->rectManager->addProperty(name);
    else if (streq(type, "enum"))
    {
        p = _d->enumManager->addProperty(name);
        std::vector<std::string> sp = StringUtil::split(choices, ";");
        QStringList enumNames;
        for (const auto& str : sp)
        {
            enumNames.append(str.c_str());
        }
        _d->enumManager->setEnumNames(p, enumNames);
    }
    setValueFromString(_d, p, type, value);

    p->setEnabled(!readyonly);

    _d->nameToProp[name] = p;
    _d->propToType[p] = type;
    _d->browser->addProperty(p);
}

#define CMD(c) \
    Router::getInstance().onValueChange(\
        _boxList->current(), property->propertyName(), c)


void XXEditor::valueChanged(QtProperty *property, double value)
{
    CMD(StringUtil::toString(value).c_str());
}

void XXEditor::valueChanged(QtProperty *property, const QString &value)
{
    CMD(value);
}

void XXEditor::valueChanged(QtProperty *property, const QColor &value)
{
    CMD(StringUtil::toString(value.red(), value.green(), value.blue(), value.alpha()).c_str());
}

void XXEditor::valueChanged(QtProperty *property, const QPointF &value)
{
    CMD(StringUtil::toString(value.x(), value.y()).c_str());
}

void XXEditor::valueChanged(QtProperty *property, const QSizeF &value)
{
    CMD(StringUtil::toString(value.width(), value.height()).c_str());
}

void XXEditor::valueChanged(QtProperty *property, bool value)
{
    CMD(StringUtil::toString(value).c_str());
}

void XXEditor::valueChanged(QtProperty *property, int value)
{
    CMD(StringUtil::toString(value).c_str());
}

void XXEditor::valueChanged(QtProperty *property, const QRectF &value)
{
    CMD(StringUtil::toString(value.x(), value.y(), value.width(), value.height()).c_str());
}

void XXEditor::onLoadFileButtonClick(QtProperty * p)
{
    QDir d;

    if (!_currentSaveFile.isEmpty() && !_currentSaveFile.isNull())
        d = QDir(QFileInfo(_currentSaveFile).dir());
    else
        d = QDir(_workPath);
    
    QString filePath = QFileDialog::getOpenFileName(window(),
        tr("Choose a File"),
        d.absolutePath(),
        tr("Images (*.png *.jpg);;Font (*.fnt);;All (*.*)"));

    if (filePath.isNull())
        return;

    QString newPath = d.relativeFilePath(filePath);

    _d->fileManager->setValue(p, newPath);
}

void XXEditor::setCurrentFile(const QString& file)
{
	if (file.isNull() || file.isEmpty())
		return;

    _currentSaveFile = file;
    QFileInfo finfo(file);
    
    CCFileUtils::sharedFileUtils()->setSearchRootPath(finfo.absoluteDir().absolutePath().toUtf8());
    setWindowTitle(QString("XXEditor - [%1]").arg(file));
}

void XXEditor::closeEvent(QCloseEvent *event)
{
    QSettings settings("XXEditor", "MyApp");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("windowState", saveState());
    settings.setValue("bgcolor", _bgColor);
    settings.setValue("_workPath", _workPath);
    settings.setValue("_scripsPath", _scripsPath);
    settings.setValue("boxVisible", Options::current.boxVisible);
    QMainWindow::closeEvent(event);
}

void XXEditor::readSettings()
{
    QSettings settings("XXEditor", "MyApp");
    restoreGeometry(settings.value("geometry").toByteArray());
    restoreState(settings.value("windowState").toByteArray());
    _bgColor = settings.value("bgcolor").value<QColor>();
    _workPath = settings.value("_workPath").toString();
    _scripsPath = settings.value("_scripsPath").toString();
    Options::current.boxVisible = settings.value("boxVisible", true).toBool();
}

void XXEditor::setBoxShow(bool checked)
{
    Options::current.boxVisible = checked;
}
